package net

import (
	"MsSgServer/utils"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/forgoer/openssl"
	"github.com/gorilla/websocket"
	"log"
	"sync"
)

// 处理 websocket 服务

// websocket 服务
type wsServer struct {
	wsConn       *websocket.Conn        // websocket 对应的链接
	router       *Router                // 路由
	outChan      chan *WsMsgRsp         // 回复信息 写队列
	Seq          int64                  // 序列
	property     map[string]interface{} // 属性
	propertyLock sync.RWMutex           // 属性的读写锁
}

func NewWsServer(wsConn *websocket.Conn) *wsServer {
	return &wsServer{
		wsConn:   wsConn,
		outChan:  make(chan *WsMsgRsp, 1000), // 有缓存的 写通道
		property: make(map[string]interface{}),
		Seq:      0,
	}
}

// Router 接口实现 Router 设置
func (w *wsServer) Router(router *Router) {
	w.router = router
}

// SetProperty 设置属性
func (w *wsServer) SetProperty(key string, value interface{}) {
	// 首先进行加锁
	w.propertyLock.Lock()
	// 结束进行解锁
	defer w.propertyLock.Unlock()
	// 修改属性
	w.property[key] = value
}

// GetProperty 获取属性
func (w *wsServer) GetProperty(key string) (interface{}, error) {
	// 采用读锁
	w.propertyLock.RLock()
	defer w.propertyLock.RUnlock()
	// 判断一下是否可以拿到	 返回读取信息
	if value, ok := w.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}

}

// RemoveProperty 删除属性
func (w *wsServer) RemoveProperty(key string) {
	// 写锁
	w.propertyLock.Lock()
	defer w.propertyLock.Unlock()
	// 原生 delete方法
	delete(w.property, key)
}

// Addr 返回地址
func (w *wsServer) Addr() string {
	// 返回远程地址
	return w.wsConn.RemoteAddr().String()
}

// Push
func (w *wsServer) Push(name string, data interface{}) {
	// 将回复的消息进行封装
	rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: 0}}
	// 封装的消息传入返回通道
	w.outChan <- rsp
}

// Start 通道一旦建立 那么收发消息就要一直监听
func (w *wsServer) Start() {
	// 启动读写数据的处理逻辑
	go w.readMsgLoop()
	go w.writeMsgLoop()
}

// 读取消息循环监听
func (w *wsServer) readMsgLoop() {
	// 先读到客户端发送过来的消息 然后进行处理 再 回消息
	// 经过路由 实际处理程序
	defer func() {
		if err := recover(); err != nil {
			log.Println("捕捉到异常:", err)
			w.Close()
		}
	}()
	for {
		// 读取发送过来的消息
		_, data, err := w.wsConn.ReadMessage()
		if err != nil {
			log.Println("收消息出现错误: ", err)
		}
		fmt.Println(data)
		// 收到消息后进行解析 前端发送过来的消息格式为 json格式
		// 1. data 解压 unzip
		data, err = utils.UnZip(data)
		if err != nil {
			log.Println("解压数据出错， 非法格式: ", err)
			// 进入下一循环
			continue
		}
		// 2. 前端消息时加密的 需要进行解密 从属性里面 拿到 加密的 secretKey
		secretKey, err := w.GetProperty("secretKey")
		// secretKey 有加密 进行解密
		if err == nil {
			// 有加密
			key := secretKey.(string)
			// 客户端传过来的数据是加密的 需要解密
			d, err := utils.AesCBCDecrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
			if err != nil {
				log.Println("数据格式有误，解密失败: ", err)
				// 出错后发起握手
				w.Handshake()
			} else {
				// 解密完成 data 重新赋值
				data = d
			}
		}
		// 3. data 转为 请求体body
		body := &ReqBody{}
		// 将链接获得的 data（json） 数据 转换成 请求体所需的格式（请求体）
		err = json.Unmarshal(data, body)
		if err != nil {
			log.Println("数据格式有误，非法格式: ", err)
		} else {
			// 获取前端传递的数据了， 拿上这些数据去具体的业务进行处理
			req := &WsMsgReq{Conn: w, Body: body}
			rsp := &WsMsgRsp{Body: &RspBody{Name: body.Name, Seq: req.Body.Seq}}
			// 将数据传入路由进行处理
			w.router.Run(req, rsp)
			// 处理完成返回响应体
			w.outChan <- rsp
		}

	}
	// 程序循环出现问题 调用关闭
	w.Close()
}

// 写入数据循环监听
func (w *wsServer) writeMsgLoop() {
	for {
		select {
		case msg := <-w.outChan:
			fmt.Println(msg)
			w.Write(msg)
		}
	}
}

func (w *wsServer) Close() {
	// 关闭通道
	_ = w.wsConn.Close()
}

// Write 写数据 处理
func (w *wsServer) Write(msg *WsMsgRsp) {
	// 将 处理完成的响应体 转成 json
	data, err := json.Marshal(msg.Body)
	if err != nil {
		log.Println(err)
	}
	// 2. 前端消息时加密的 需要进行解密 从属性里面 拿到 加密的 secretKey
	secretKey, err := w.GetProperty("secretKey")
	// secretKey 有加密 进行解密
	if err == nil {
		// 有加密
		key := secretKey.(string)
		// 数据做加密
		data, _ = utils.AesCBCEncrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
	}
	// 加密完成之后进行压缩
	if data, err := utils.Zip(data); err == nil {
		// 压缩完成之后 进行写数据
		w.wsConn.WriteMessage(websocket.BinaryMessage, data)
	}
}

// HandshakeMsg 常量
const HandshakeMsg = "handshake"

// Handshake 握手协议 当游戏客户端 发送请求的时候 会先进行握手协议
// 后端会发送预定的加密key 给客户端
// 客户端在发送数据的时候会使用该 key 进行加密
func (w *wsServer) Handshake() {
	secretKey := ""
	// 获取当前设置的 key
	key, err := w.GetProperty("secretKey")
	if err == nil {
		// 若有设置 secret key 值
		secretKey = key.(string)
	} else {
		// 若没有设置 secret key 值 随机生成
		secretKey = utils.RandSeq(16)
	}
	// 封装一个 message
	handshake := &Handshake{Key: secretKey}
	// 返回信息
	body := &RspBody{Name: HandshakeMsg, Msg: handshake}
	// 信息转json
	if data, err := json.Marshal(body); err == nil {
		if secretKey != "" {
			// secretKey 本次随机生成 该次握手则设置
			w.SetProperty("secretKey", secretKey)
		} else {
			// secretKey 未设置 （表示之前步骤存在问题）
			w.RemoveProperty("secretKey")
		}
		if data, err := utils.Zip(data); err == nil {
			w.wsConn.WriteMessage(websocket.BinaryMessage, data)
		}
	}
}
